<?php

class Account_Model_Password
{
    const ACCOUNT_NOT_FOUND = 'accountNotFound';
    const ACCOUNT_INACTIVE = 'accountInactive';
	const PASSWORDS_MISMATCH = 'passwordsMismatch';
	// No account associated with the password key
	const KEY_NOT_FOUND = 'keyNotFound';
	const KEY_EXPIRED = 'keyExpired';


	protected $_errorCodes = array();


	public function getErrorCodes()
	{
		return $this->_errorCodes;
	}


	public function beginPasswordReset($identity = null)
	{
		$this->_errorCodes = array();

		if (empty($identity)) {
			throw new Exception('Empty identity');
		}


		$key = 'idname';
		// Check if the input is a valid email address
		$emailAddressValidator = new Zend_Validate_EmailAddress();
		if ($emailAddressValidator->isValid($identity)) {
			$key = 'email';
		}


		$opts = Kernel::getInstance()->getOptions();


		$accountTbl = Account_Model_DbTable_Account::getInstance();

		$select = $accountTbl->select();
		$select->where($key .'= ?', $identity);

		$accountDbRes = $accountTbl->fetchAll($select);

		if ($accountDbRes->count() !== 1) {
			$this->_errorCodes[] = self::ACCOUNT_NOT_FOUND;
			return FALSE;
		}


		$accountDbRow = $accountDbRes->getRow(0);
		$accountId = $accountDbRow->id;


		$statusTbl = Account_Model_DbTable_Status::getInstance();

		$select = $statusTbl->select();
		$select->where('account_id= ?', $accountId);

		$statusDbRes = $statusTbl->fetchAll($select);

		$statuses = array();
		foreach ($statusDbRes as $statusDbRow) {
			$statuses[] = $statusDbRow->status;
		}

		//TODO: move into AccountStatus
		if (in_array('pending', $statuses) || in_array('suspended', $statuses) || in_array('banned', $statuses)) {
			$this->_errorCodes[] = self::ACCOUNT_INACTIVE;
			return FALSE;
		}


		$pwresetKey = md5(sha1(microtime()));


		$statusDbData = array(
			'account_id'=> $accountId,
			'assign_time'  => time() - date('Z'),
			'status'    => 'pwreset',
			'param'     => $pwresetKey,
		);

		$insertId = $statusTbl->insert($statusDbData);
		if (intval($insertId) === 0) {
			throw new Exception('Query error');
		}


		$site_name = Kernel::getInstance()->getSiteName();
		$site_url = site_url();
		$username = $accountDbRow->nicename;
		$identity = $accountDbRow->nicename;
		$usermail = $accountDbRow->email;
		$password_reset_key = $pwresetKey;
		$password_reset_url = site_url() .'/account/pwreset';
		$password_reset_urlkey = $password_reset_url .'/code/'. $password_reset_key;

		$site_email = $opts['site']['administrator']['email'];

		ob_start();
		require CORE_PATH .'/account/mails/PasswordResetKey.php';
		$mailBody = ob_get_clean();

		//DEV: email address override
		@include '../mailaddr.php';

		$mail = new Euso_Model_Mail();
		$mail->setBodyText($mailBody)
			->setFrom($site_email, $site_name)
			->addTo($usermail, $accountDbRow->nicename)
			->setSubject($site_name .' Account Password Reset Request')
		;

		$mail->send();


		return TRUE;
	}

	public function execPasswordReset($pwresetKey = NULL)
	{
		$this->_errorCodes = array();

		if (empty($pwresetKey)) {
			throw new Exception('Empty password reset key');
		}


		$opts = Kernel::getInstance()->getOptions();


		$statusTbl = Account_Model_DbTable_Status::getInstance();

		$select = $statusTbl->select();
		$select
			->where('status= ?', 'pwreset')
			->where('param= ?', $pwresetKey)
		;

		$dbResult = $statusTbl->fetchAll($select);

		if ($dbResult->count() < 1) {
			$this->_errorCodes[] = self::KEY_NOT_FOUND;
			return FALSE;
		}

		$statusDbRow = $dbResult->getRow(0);
		$accountId = $statusDbRow->account_id;


		$updRows = $statusTbl->delete(array(
			'account_id= ?' => $accountId,
			'param= ?' => $pwresetKey,
		));

		if ($updRows < 1) {
			throw new Exception('Query error');
		}


		// Check for expiration
		// If the key is expired, set the error flag and then just return false

		$expiryTime = 21600;
		if (is_array($opts['account']) && is_array($opts['account']['pwreset'])
			&& isset($opts['account']['pwreset']['expiry'])) {
			$expiryTime = intval($opts['account']['pwreset']['expiry']);
		}

		if ($expiryTime > 0 && intval($statusDbRow->assign_time) + $expiryTime < intval(time() - date('Z'))) {
			$this->_errorCodes[] = self::KEY_EXPIRED;
			return FALSE;
		}


		$accountTbl = Account_Model_DbTable_Account::getInstance();

		$select = $accountTbl->select();
		$select->where('id= ?', $accountId);

		$accountDbRes = $accountTbl->fetchAll($select);

		if ($accountDbRes->count() !== 1) {
			throw new Exception('A single account expected');
		}


		$accountDbRow = $accountDbRes->getRow(0);

		$newPassword = $this->_generatePassword();


		$dbData = array(
			'passkey' => md5($accountDbRow->salt . sha1($newPassword)),
		);

		$updRows = $accountTbl->update($dbData, array(
			'id= ?' => $accountId
		));

		if ($updRows != 1) {
			throw new Exception('Query failed');
		}


		$site_name = Kernel::getInstance()->getSiteName();
		$site_url = site_url();
		$username = $accountDbRow->nicename;
		$identity = $accountDbRow->nicename;
		$usermail = $accountDbRow->email;
		$password = $newPassword;

		$site_email = $opts['site']['administrator']['email'];

		ob_start();
		require CORE_PATH .'/account/mails/PasswordReset.php';
		$mailBody = ob_get_clean();

		//DEV: email address override
		@include '../mailaddr.php';

		$mail = new Euso_Model_Mail();
		$mail->setBodyText($mailBody)
			->setFrom($site_email, $site_name)
			->addTo($usermail, $accountDbRow->nicename)
			->setSubject($site_name .' Account Password Reset')
		;

		$mail->send();


		return TRUE;
	}


	public function changePassword($identity = NULL, $pwdold = NULL, $pwdnew = NULL)
	{
		$this->_errorCodes = array();

		if (empty($identity) === TRUE) {
			throw new Exception('Empty identity');
		}


		$opts = Kernel::getInstance()->getOptions();


		$accountTbl = Account_Model_DbTable_Account::getInstance();

		$key = 'idname';
		// Check if the input is a valid email address
		$emailAddressValidator = new Zend_Validate_EmailAddress();
		if ($emailAddressValidator->isValid($identity)) {
			$key = 'email';
		}

		$select = $accountTbl->select();
		$select->where($key .'= ?', $identity);

		$accountDbRes = $accountTbl->fetchAll($select);

		if ($accountDbRes->count() !== 1) {
			$this->_errorCodes[] = self::ACCOUNT_NOT_FOUND;
			return FALSE;
		}


		$accountDbRow = $accountDbRes->getRow(0);


		if (md5($accountDbRow->salt . sha1($pwdold)) != $accountDbRow->passkey) {
			$this->_errorCodes[] = self::PASSWORDS_MISMATCH;
			return FALSE;
		}


		$newPasswordHashed = md5($accountDbRow->salt . sha1($pwdnew));

		$dbData = array(
			'passkey' => $newPasswordHashed,
		);

		$updRows = $accountTbl->update($dbData, array(
			'id= ?' => $accountDbRow->id
		));

		if ($updRows != 1) {
			throw new Exception('Query failed');
		}


		$site_name = Kernel::getInstance()->getSiteName();
		$site_url = site_url();
		$username = $accountDbRow->nicename;
		$identity = $accountDbRow->nicename;
		$usermail = $accountDbRow->email;
		$password = $pwdnew;

		$site_email = $opts['site']['administrator']['email'];

		ob_start();
		require CORE_PATH .'/account/mails/PasswordNew.php';
		$mailBody = ob_get_clean();

		//DEV: email address override
		@include '../mailaddr.php';

		$mail = new Euso_Model_Mail();
		$mail->setBodyText($mailBody)
			->setFrom($site_email, $site_name)
			->addTo($usermail, $accountDbRow->nicename)
			->setSubject($site_name .' Account Password Changed')
		;

		$mail->send();


		return TRUE;
	}


	protected function _generatePassword()
	{
		$seed1 = 'aeiouAEIOU123456789!#$%&-=123456789!#$%&-=';
		$seed2 = 'bcdfghjklmnpqrstvwxyzBCDFGHJKLMNPQRSTVWXYZ';

		$password = '';
		$alt = time() % 2;
		for ($i = 0; $i < 9; $i++) {
			if ($alt == 1) {
				$password .= $seed2[(rand() % strlen($seed2))];
				$alt = 0;
			} else {
				$password .= $seed1[(rand() % strlen($seed1))];
				$alt = 1;
			}
		}

		return $password;
	}
}